package org.dolphin.dao.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.dolphin.base.bean.FlatProductVO;
import org.dolphin.dao.ProductDao;
import org.dolphin.dao.util.SessionHelper;
import org.dolphin.entity.inventory.Product;
import org.dolphin.entity.inventory.ProductAttribute;
import org.dolphin.entity.inventory.ProductCategoryLink;
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.sql.JoinType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository(value = "productDao")
public class ProductDaoImpl implements ProductDao {
  @Autowired(required = true)
  private SessionFactory sessionFactory;

  protected DetachedCriteria buildProductCriteria() {
    DetachedCriteria dc = DetachedCriteria.forClass(Product.class);
    return dc;
  }

  @Override
  public Product fetchProduct(Serializable id) {
    Product product = fetchProduct(id, false);
    return product;
  }

  @SuppressWarnings("unchecked")
  @Override
  public Product fetchProduct(Serializable id, boolean fetchJoinProductCategory) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    DetachedCriteria dc = buildProductCriteria();
    dc.add(Restrictions.idEq(id));
    if (fetchJoinProductCategory) {
      dc.createCriteria("productCategoryLinks", "links", JoinType.LEFT_OUTER_JOIN)
          .setFetchMode("links", FetchMode.JOIN)
          .createCriteria("pk.category", "c", JoinType.INNER_JOIN)
          .setFetchMode("c", FetchMode.JOIN);
    }
    Criteria criteria = dc.getExecutableCriteria(session);
    List<Product> list = criteria.list();
    if (list != null && !list.isEmpty()) {
      return list.get(0);
    }
    return null;
  }

  @Override
  public Product getProduct(String name) {
    Product product = getProduct(name, null);
    return product;
  }

  @SuppressWarnings("unchecked")
  @Override
  public Product getProduct(String name, Serializable excludedId) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    DetachedCriteria dc = buildProductCriteria();
    dc.add(Restrictions.eq("name", name));
    if (excludedId != null) {
      dc.add(Restrictions.not(Restrictions.idEq(excludedId)));
    }
    Criteria criteria = dc.getExecutableCriteria(session);
    List<Product> list = criteria.list();
    if (list != null && !list.isEmpty()) {
      return list.get(0);
    }
    return null;
  }

  @SuppressWarnings("unchecked")
  @Override
  public List<FlatProductVO> getProducts(int firstResult, int maxResults, List<Order> orders) {
    DetachedCriteria dc = buildProductCriteria();
    if (orders != null && !orders.isEmpty()) {
      for (Order order : orders) {
        dc.addOrder(order);
      }
    }
    dc.setFetchMode("productCategoryLinks", FetchMode.JOIN).createAlias("productCategoryLinks",
        "link", JoinType.LEFT_OUTER_JOIN);
    dc.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    Criteria criteria = dc.getExecutableCriteria(session);
    if (firstResult > 0) {
      criteria.setFirstResult(firstResult);
    }
    if (maxResults > 0) {
      criteria.setMaxResults(maxResults);
    }
    List<Product> list = criteria.list();
    List<FlatProductVO> results = new ArrayList<FlatProductVO>(list != null ? list.size() : 0);
    if (list != null && !list.isEmpty()) {
      for (Product product : list) {
        List<ProductAttribute> productAttributes = product.getProductAttributes();
        int productAttributeCount = productAttributes != null ? productAttributes.size() : 0;
        FlatProductVO vo = new FlatProductVO();
        vo.setId(product.getId());
        vo.setName(product.getName());
        vo.setVersion(product.getVersion());
        vo.setProductAttributeCount(productAttributeCount);
        List<ProductCategoryLink> categoryLinks = product.getProductCategoryLinks();
        String categories = "";
        if (categoryLinks != null && !categoryLinks.isEmpty()) {
          for (int i = 0; i < categoryLinks.size(); i++) {
            ProductCategoryLink link = categoryLinks.get(i);
            String name = link.getCategory().getName();
            categories += name;
            if (i < categoryLinks.size() - 1) {
              categories += ", ";
            }
          }
        }
        vo.setCategories(categories);
        results.add(vo);
      }
    }
    return results;
  }

  @Override
  public Long getProductsCount() {
    DetachedCriteria dc = buildProductCriteria();
    dc.setProjection(Projections.rowCount());
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    Criteria criteria = dc.getExecutableCriteria(session);
    Number totalRecords = (Number) criteria.uniqueResult();
    return totalRecords.longValue();
  }

  @Override
  public Product loadProduct(Serializable id) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    Product product = (Product) session.load(Product.class, id);
    return product;
  }

  @Override
  public void saveProduct(Product product) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    session.save(product);
  }

  @Override
  public void updateProduct(Product product) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    session.update(product);
  }
}
